package com.wanxi.dangdang.util;

import com.wanxi.dangdang.common.CommonResult;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;

public class UploadUtil {
    /*
    使用方法
        UploadUtil uploadUtil = new UploadUtil(file);
        uploadUtil.setBasePath("C:\\Users\\admin\\Desktop\\webapp\\frontstage\\img"); //硬盘地址
        uploadUtil.setBaseUrl("http://image.api/frontstage/img"); //返回url地址
        uploadUtil.setAllowRename(false);//是否重命名
        uploadUtil.setDirName("house");//设置对应的文件夹名字
        uploadUtil.setExtNames("png,jpg");//后缀接收
        CommonResult uploadData = null;
        try {
            uploadData = uploadUtil.upload();
        } catch (Exception e) {
            e.printStackTrace();
        }
     */
    //文件保存跟路径
    private String basePath = System.getProperty("user.dir");//D:\tomcat\apache-tomcat-9.0.46\bin

    //根路径下的 文件目录
    private String dirName;

    //文件访问的 根路径
    private String baseUrl;

    //单个文件的最大大小
    private Long fileSizeMax = 1024 * 1024 * 3L;

    //所有文件大小最大之和
    private Long sizeMax = 1204 * 1024 * 10L;

    //允许上传文件的 后缀名（逗号","隔开）
    private String extNames;

    //允许上传文件重命名 默认为 true
    private boolean allowRename = true;

    //允许文件重命名  名称
    private String fileRename;

    //上传文件临时路径
    private String tempPath = System.getProperty("java.io.tmpdir");

    public String getBasePath() {
        return basePath;
    }

    public void setBasePath(String basePath) {
        this.basePath = basePath;
    }

    public String getDirName() {
        return dirName;
    }

    public void setDirName(String dirName) {
        this.dirName = dirName;
    }

    public String getBaseUrl() {
        return baseUrl;
    }

    public void setBaseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    public Long getFileSizeMax() {
        return fileSizeMax;
    }

    public void setFileSizeMax(Long fileSizeMax) {
        this.fileSizeMax = fileSizeMax;
    }

    public Long getSizeMax() {
        return sizeMax;
    }

    public void setSizeMax(Long sizeMax) {
        this.sizeMax = sizeMax;
    }

    public String getExtNames() {
        return extNames;
    }

    public void setExtNames(String extNames) {
        this.extNames = extNames;
    }

    public boolean isAllowRename() {
        return allowRename;
    }

    public void setAllowRename(boolean allowRename) {
        this.allowRename = allowRename;
    }

    public String getFileRename() {
        return fileRename;
    }

    public void setFileRename(String fileRename) {
        this.fileRename = fileRename;
    }

    public String getTempPath() {
        return tempPath;
    }

    public void setTempPath(String tempPath) {
        this.tempPath = tempPath;
    }

    /*---------------------------------------------------------------------*/

    //最终文件名
    private String newFileName;

    //所有文件的实际大小
    private Long filesSize = 0L;

    //文件保存目录路径
    private String savePath;

    //（不带域名及项目路径）文件访问路径（不包括文件名），中间变量，用于组装最终的访问URL
    private String saveUrl;

    //（不带域名及项目路径）文件最终访问路径（包括文件名）
    private String fileUrl;

    //请求中普通表单字段
    private Map<String,String> fields = new HashMap<>();

    //请求中文件类型字段
    private List<MultipartFile> fileList = new ArrayList<>();

    //上传错误信息
    private String errorMsg;

    public String getNewFileName() {
        return newFileName;
    }

    public Long getFilesSize() {
        return filesSize;
    }

    public String getSavePath() {
        return savePath;
    }

    public String getSaveUrl() {
        return saveUrl;
    }

    public String getFileUrl() {
        return fileUrl;
    }

    public Map<String, String> getFields() {
        return fields;
    }

    public List<MultipartFile> getFileList() {
        return fileList;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public List<FileResult> getUpLoadResults() {
        return upLoadResults;
    }

    public HttpServletRequest getReq() {
        return req;
    }

    //每个文件上传最终结果
    private List<FileResult> upLoadResults = new ArrayList<>();

    private HttpServletRequest req = null;
    MultipartFile[] file = null;

    public UploadUtil(HttpServletRequest req) {
        this.req = req;
    }

    public UploadUtil(MultipartFile[] file) {
        this.file = file;
    }

    /**
     * 文件上传
     */
    public CommonResult upload() throws Exception{

        //验证初始化表单
        this.initCheckDir();
        if (StringUtils.isNotBlank(this.errorMsg)){
            return CommonResult.failure(500,this.errorMsg);
        }
        this.initFieldAndCheckFile();
        if (StringUtils.isNotBlank(this.errorMsg)){
            return CommonResult.failure(500,this.errorMsg);
        }

        //上传文件
        if(! this.fileList.isEmpty()){
            if (this.fileList.size() >1){
                //多个文件不能重名
                this.allowRename = false;
            }

            //保存上传文件
            for (MultipartFile item : this.fileList){
                FileResult result = new FileResult();
                result.setOldFileName(item.getName());
                result.setFieldName(item.getOriginalFilename());
                this.saveFile(item);//保存文件
                result.setSavePath(this.savePath + this.newFileName);
                result.setFileUrl(this.saveUrl + this.newFileName);
                result.setNewFileName(this.newFileName);
                upLoadResults.add(result);
            }
        }else {
            return CommonResult.failure(500,"没有发现文件");
        }

        if (StringUtils.isBlank(this.errorMsg)){
            return CommonResult.success("上传文件",upLoadResults);
        }else {
            return CommonResult.failure("上传失败",upLoadResults);
        }
    }

    /**
     * 上传前验证，及初始化文件目录
     * 描述：验证-1
     */
    private void initCheckDir(){
        //文件保存目录路径
        this.savePath = this.basePath + "/";
        //文件保存目录URL
        this.saveUrl = this.baseUrl + "/";
        //文件存放文件夹目录
        File uploadDir = new File(this.savePath);
        //获取文件内容类型
        if(file.length<1){
            this.errorMsg = "请选择文件";
        }else if (!uploadDir.isDirectory()){
            this.errorMsg = "上传目录[" + this.savePath + "]不存在";
        }else if (!uploadDir.canWrite()){
            this.errorMsg = "上传目录[" + this.savePath + "]没有写权限";
        }else {
            //创建文件夹 basePath/dirName/
            this.savePath += this.dirName + File.separator;
            this.saveUrl += this.dirName + File.separator;
            File saveDirFile = new File(this.savePath);
            if (!saveDirFile.exists()){
                saveDirFile.mkdirs();
            }
            //获取上传临时路径
            this.tempPath += File.separator;
            File file = new File(this.tempPath);
            if (!file.exists()){
                file.mkdirs();
            }
            //统一路径分割符
            this.savePath = this.savePath.replace("/",File.separator)
                    .replace("//",File.separator)
                    .replace("\\",File.separator)
                    .replace("\\\\",File.separator);
            this.saveUrl = this.saveUrl.replace("\\","/");
//            this.saveUrl = this.saveUrl.replace("\\","/").replace("//","/");
        }

    }

    /**
     * 初始化请求中的表单属性字段，验证文件字段
     * 描述：验证-2
     * 描述：返回表单中一般属性字段和文件属性字段
     */
    private void initFieldAndCheckFile(){
        //判断req是否是文件上传请求
//        if (ServletFileUpload.isMultipartContent(this.req)){
        if (file.length>=1){
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //阈值，超过这个值才会写到零时目录，否则在内存中
            factory.setSizeThreshold(1024 * 1024 * 10);
            factory.setRepository(new File(this.tempPath));
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setHeaderEncoding("UTF-8");
            //单个文件上传最大限制
            if (this.fileSizeMax > 0L){
                upload.setFileSizeMax(this.fileSizeMax);
            }
            //所有文件的最大限制
            if (this.sizeMax > 0L){
                upload.setSizeMax(this.sizeMax);
            }
            List<MultipartFile> items = Arrays.asList(file);

            if (items != null && !items.isEmpty()){
                Iterator<MultipartFile> iter = items.iterator();
                while (iter.hasNext()){
                    MultipartFile item = iter.next();
                    String fileName = item.getOriginalFilename();
                    String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
                    //文件类字段
                    this.filesSize +=item.getSize();
                    if (this.extNames != null && !Arrays.<String> asList(this.extNames.split(",")).contains(fileExt)){//检查扩展名（后缀名）
                        this.errorMsg += "上传文件扩展名是不允许的扩展名。\n只允许 " + this.extNames + " 格式 ";
                    }else {
                        this.fileList.add(item);
                    }
                }
            }


        }
    }

    /**
     * 保存文件
     * @param item
     * @throws Exception
     */
    private void saveFile(MultipartFile item) throws Exception{
        String oldFileName = item.getOriginalFilename();
        String fileExt = oldFileName.substring(oldFileName.lastIndexOf(".") + 1).toLowerCase();
        //允许重命名
        if (this.allowRename){
            //重命名字段为null或者重命名后的文件已存在则自动生成新的文件名
            if (StringUtils.isBlank(this.fileRename)||new File(this.savePath,this.fileRename + fileExt).exists()){
                SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                this.newFileName = df.format(new Date()) + "_" +(new Random().nextInt(889) + 100) + "." + fileExt;
            }else {
                this.newFileName = this.fileRename + "." + fileExt;
            }
        }else {
            //如果原始文件名已存在则覆盖原文件
            this.newFileName = oldFileName;
        }

        /*if (this.allowRename){
            //重命名字段为null或者重命名后的文件已存在则自动生成新的文件名
            if (StringUtils.isBlank(this.fileRename)||new File(this.savePath,this.fileRename + fileExt).exists()){
                SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                this.newFileName = df.format(new Date()) + "_" +(new Random().nextInt(889) + 100) + "." + fileExt;
            }else {
                this.newFileName = this.fileRename + "." + fileExt;
            }
        }else {
            //如果原始文件名已存在则自动生成文件名
            if (new File(this.savePath,oldFileName).exists()){
                SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                this.newFileName = df.format(new Date()) + "_" +(new Random().nextInt(889) + 100) + "." + fileExt;
            }else {
                this.newFileName = oldFileName;
            }
        }*/   //文件重命名代码（原代码）

        File uploadedFile  = new File(this.savePath, this.newFileName);
        //写入文件
        item.transferTo(uploadedFile);
    }

    /**
     * 每个文件上传结果
     */
    public static class FileResult{
        //文件保存绝对路径（包含文件名）
        private String savePath;
        //文件访问路径（包含文件名）
        private String fileUrl;
        //文件上传的原始名字
        private String oldFileName;
        //文件最终保存的文件名
        private String newFileName;
        //表单文本框（input[type=file]）的name属性
        private String fieldName;

        public String getSavePath() {
            return savePath;
        }

        public void setSavePath(String savePath) {
            this.savePath = savePath;
        }

        public String getFileUrl() {
            return fileUrl;
        }

        public void setFileUrl(String fileUrl) {
            this.fileUrl = fileUrl;
        }

        public String getOldFileName() {
            return oldFileName;
        }

        public void setOldFileName(String oldFileName) {
            this.oldFileName = oldFileName;
        }

        public String getNewFileName() {
            return newFileName;
        }

        public void setNewFileName(String newFileName) {
            this.newFileName = newFileName;
        }

        public String getFieldName() {
            return fieldName;
        }

        public void setFieldName(String fieldName) {
            this.fieldName = fieldName;
        }
    }
}
